package com.irdstudio.basic.sequence.service.impl;

import static com.irdstudio.basic.sequence.service.DefaultPatternDescriptor.DAY;
import static com.irdstudio.basic.sequence.service.DefaultPatternDescriptor.HOUR;
import static com.irdstudio.basic.sequence.service.DefaultPatternDescriptor.MIN;
import static com.irdstudio.basic.sequence.service.DefaultPatternDescriptor.MONTH;
import static com.irdstudio.basic.sequence.service.DefaultPatternDescriptor.SECOND;
import static com.irdstudio.basic.sequence.service.DefaultPatternDescriptor.YEAR_2;
import static com.irdstudio.basic.sequence.service.DefaultPatternDescriptor.YEAR_4;
import static com.irdstudio.basic.sequence.service.util.PatternDescriptorHelper.padZeroIfNecessary;

import java.time.LocalDateTime;
import java.util.Optional;

import com.irdstudio.basic.sequence.service.DefaultPatternDescriptor;
import com.irdstudio.basic.sequence.service.PatternDescriptor;
import com.irdstudio.basic.sequence.service.PatternResolver;
import com.irdstudio.basic.sequence.service.facade.ClassableSeqService;
import com.irdstudio.basic.sequence.service.facade.LimitableSeqService;
import com.irdstudio.basic.sequence.service.facade.PatternedLimitableSeqService;
import com.irdstudio.basic.sequence.service.facade.SeqService;
import com.irdstudio.basic.sequence.service.util.PatternDescriptorHelper;

/**
 * @description:可以进行Pattern字符串解析的序列号服务
 * @date:2019年2月13日 下午5:10:29
 * @author:hengyh
 */
public class PatternedLimitableSeqServiceImpl implements PatternedLimitableSeqService {

    /** the ID generator to generate unclassifiable sequence **/
    private SeqService unClassableSeqGen;

    /** the ID generator to generate classifiable sequence **/
    private ClassableSeqService classableSeqGen;

    /** the ID generator to generate sequence with a specific length **/
    private LimitableSeqService limitSeqGen;

    /** the resolver to resolve the pattern, default is PatternedLimitableSeqServiceImpl#InternalPatternResolver **/
    private PatternResolver patternResolver = new InternalPatternResolver();

    public PatternedLimitableSeqServiceImpl(
            SeqService unClassableSeqGen,
            LimitableSeqService limitableSeqGen) {
        this(unClassableSeqGen, limitableSeqGen, limitableSeqGen);
    }

    public PatternedLimitableSeqServiceImpl(
            SeqService unClassableSeqGen,
            ClassableSeqService classableSeqGen,
            LimitableSeqService limitableSeqGen) {
        this(unClassableSeqGen, classableSeqGen, limitableSeqGen, null);
    }

    public PatternedLimitableSeqServiceImpl(
            SeqService unClassableSeqGen,
            ClassableSeqService classableSeqGen,
            LimitableSeqService limitableSeqGen,
            PatternResolver patternResolver) {
        this.unClassableSeqGen = unClassableSeqGen;
        this.classableSeqGen = classableSeqGen;
        this.limitSeqGen = limitableSeqGen;
        this.patternResolver = Optional.ofNullable(patternResolver).orElse(this.patternResolver);
    }

    /*---------------------------------------------------------------*/
    /*          Implementation of PatternedLimitableSeqService       */
    /*---------------------------------------------------------------*/

    private static final String NO_KEY = null;
    
    @Override
    public String getSequence() { return unClassableSeqGen.getSequence(); }

    @Override
    public String getSequence(String key) { return classableSeqGen.getSequence(key); }

    @Override
    public String getLimitSequence(String key, Integer length) {
        return limitSeqGen.getLimitSequence(key, length);
    }

    @Override
    public String getLimitSequence(String key, Long maxValue) {
        return limitSeqGen.getLimitSequence(key, maxValue);
    }
    
    @Override
    public String getPatternedSequence(String patternStr) {
        PatternDescriptor resolved = this.patternResolver.resolve(patternStr);
        return doGetPatternedSequence(NO_KEY, resolved);
    }
    
    @Override
    public String getPatternedSequence(PatternDescriptor descriptor) {
        PatternDescriptor resolved = this.patternResolver.resolve(descriptor);
        return doGetPatternedSequence(NO_KEY, resolved);
    }

    @Override
    public String getPatternedSequence(String key, String patternStr, Integer seqLength) {
        PatternDescriptor resolved = this.patternResolver.resolve(patternStr, seqLength);
        return doGetPatternedSequence(key, resolved);
    }

    @Override
    public String getPatternedSequence(String key, PatternDescriptor descriptor) {
    	PatternDescriptor resolved = this.patternResolver.resolve(descriptor);
    	return doGetPatternedSequence(key, resolved);
    }

    /*---------------------------------------------------------------*/
    /*             			 private methods        				 */
    /*---------------------------------------------------------------*/
    
    private String doGetPatternedSequence(String key, PatternDescriptor resolved) {
        String uid;
        if (key == NO_KEY) { // 无分类
			uid = this.getSequence();
		} else if (resolved.getSeqMaxValue() == null) { // 没有最大序列号限制
            uid = this.getSequence(key);
        } else {
            uid = this.getLimitSequence(key, resolved.getSeqMaxValue());
        }
        return PatternDescriptorHelper.getSeqFromPatternAndUid(resolved, uid);
    }

    /*---------------------------------------------------------------*/
    /*             Internal Implementation of PatternResolver        */
    /*---------------------------------------------------------------*/

    private static class InternalPatternResolver implements PatternResolver {

    	@Override
		public PatternDescriptor resolve(String patternStr) {
			return resolve(patternStr, null);
		}
    	
    	@Override
		public PatternDescriptor resolve(PatternDescriptor unResolved) {
			return resolve(unResolved.getOriginPatternString(), unResolved.getSeqLength());
		}
    	
        @Override
        public PatternDescriptor resolve(String patternStr, Integer seqLength) {
            LocalDateTime now = LocalDateTime.now();
            String resolvedString =
                    patternStr
                    .replace(YEAR_4, String.valueOf(now.getYear()))
                    .replace(YEAR_2, String.valueOf(now.getYear()).substring(2))
                    .replace(MONTH, padZeroIfNecessary(String.valueOf(now.getMonth().getValue()), 2))
                    .replace(DAY, padZeroIfNecessary(String.valueOf(now.getDayOfMonth()), 2))
                    .replace(HOUR, padZeroIfNecessary(String.valueOf(now.getHour()), 2))
                    .replace(MIN, padZeroIfNecessary(String.valueOf(now.getMinute()), 2))
                    .replace(SECOND, padZeroIfNecessary(String.valueOf(now.getSecond()), 2));

            DefaultPatternDescriptor descriptor = 
            		new DefaultPatternDescriptor(
            				resolvedString,
		                    DefaultPatternDescriptor.SPLIT,
		                    PatternDescriptorHelper.getMaxValueFromSeqLength(seqLength),
		                    seqLength);
            descriptor.setOriginPatternString(patternStr);
            return descriptor;
        }
    }

}
